package com.sikaryofficial.backend.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sikaryofficial.backend.config.PointsRuleConfig;
import com.sikaryofficial.backend.constant.PointsConstant;
import com.sikaryofficial.backend.domain.dto.req.points.PointsRuleReq;
import com.sikaryofficial.backend.domain.dto.resp.points.PointsRuleDTO;
import com.sikaryofficial.backend.domain.dto.resp.points.PointsRuleTaskDTO;
import com.sikaryofficial.backend.domain.dto.resp.points.UserPointsTaskDTO;
import com.sikaryofficial.backend.domain.entity.Article;
import com.sikaryofficial.backend.domain.entity.ArticleTag;
import com.sikaryofficial.backend.domain.entity.PointsDetail;
import com.sikaryofficial.backend.domain.entity.PointsRule;
import com.sikaryofficial.backend.domain.mapping.PointsRuleMapping;
import com.sikaryofficial.backend.service.IArticleService;
import com.sikaryofficial.backend.service.IArticleTagService;
import com.sikaryofficial.backend.service.IPointsDetailService;
import com.sikaryofficial.backend.service.IPointsDetailStrategy;
import com.sikaryofficial.backend.service.IPointsRuleService;
import com.sikaryofficial.backend.service.points.cache.PointsRuleConfigCacheService;
import com.sikaryofficial.common.core.constant.PointRuleConstant;
import com.sikaryofficial.common.core.domain.R;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.web.page.PageDomain;
import com.sikaryofficial.common.security.utils.SecurityUtils;
import com.sikaryofficial.system.api.RemoteDictService;
import com.sikaryofficial.system.api.domain.SysDictData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * @author : qinjinyuan
 * @desc : 积分规则服务
 * @date : 2023/10/30 15:39
 */
@Service
@Slf4j
public class PointsRuleManager {

    private static final Integer RULE_DISABLE_FLAG = 1;

    @Autowired
    private IPointsRuleService pointsRuleService;
    @Resource
    private PointsRuleConfigCacheService pointRuleConfigCacheService;
    @Resource
    private Map<String, IPointsDetailStrategy> pointsDetailStrategyMap;
    @Autowired
    private IPointsDetailService pointsDetailService;

    @Resource
    private RemoteDictService dictService;

    @Autowired
    private PointsRuleConfig pointsRuleConfig;
	@Autowired
	private IArticleTagService articleTagService;
    @Autowired
    private IArticleService articleService;

    public List<PointsRuleDTO> listFromCache() {
        return pointRuleConfigCacheService.getAllPointsRuleConfig();
    }

    public List<PointsRuleTaskDTO> listRuleTask() {
        // 先查所有启用的规则
        List<PointsRuleDTO> pointsRules = pointsRuleService.selectRules();
        Long userId = SecurityUtils.getUserId();
        List<PointsDetail> pointsDetails = pointsDetailService.lambdaQuery().eq(PointsDetail::getCreatedBy, userId).eq(PointsDetail::getDeletedVersion, 0).list();
        Map<String, List<PointsDetail>> ruleCompleteTimes = pointsDetails.stream().collect(Collectors.groupingBy(PointsDetail::getSourceCode));
        List<PointsRuleTaskDTO> pointsRuleTasks = new ArrayList<>();
        R<List<SysDictData>> dictDataResult = dictService.dictType(PointsConstant.POINTS_RULE_SOURCE_PATH_DICT_TYPE);
        List<SysDictData> dictDataList = dictDataResult.getData();
        Map<String, String> ruleSourcePathMap = dictDataList.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        pointsRules.forEach(item -> {
            PointsRuleTaskDTO pointsRuleTaskDTO = PointsRuleMapping.INSTANCE.coverDtoToTask(item);
            List<PointsDetail> usedRecords = ruleCompleteTimes.getOrDefault(item.getRuleCode(), Collections.emptyList());
            dealRemainderTimes(item, pointsRuleTaskDTO, usedRecords);
            pointsRuleTaskDTO.setRuleTaskName(ruleSourcePathMap.get(item.getRuleCode()));
            if (CollUtil.isNotEmpty(pointsRuleConfig.getLabeledRuleList()) && pointsRuleConfig.getLabeledRuleList().contains(pointsRuleTaskDTO.getRuleCode())) {
                ArticleTag articleTag = articleTagService.getEnabledArticleTag();
                if (articleTag != null) {
                    Article tagArticle = articleService.getById(articleTag.getArticleId());
                    pointsRuleTaskDTO.setTopic(Objects.isNull(tagArticle) ? null : tagArticle.getHeadline());
                }
            }
            pointsRuleTasks.add(pointsRuleTaskDTO);
        });
        return pointsRuleTasks;
    }




    /**
     * 分页列表查询
     *
     * @param req 分页参数
     * @return 分页列表
     */
    public IPage<PointsRuleDTO> listPage(PageDomain req) {
        Page<PointsRule> customerPage = new Page<>(req.getPageNum(), req.getPageSize());
        LambdaQueryWrapper<PointsRule> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PointsRule::getDeletedVersion, 0L);
        queryWrapper.orderByDesc(PointsRule::getCreatedTime);
        IPage<PointsRule> storeIPage = pointsRuleService.page(customerPage, queryWrapper);
        if (Objects.isNull(storeIPage) || CollUtil.isEmpty(storeIPage.getRecords())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0);
        }
        return storeIPage.convert(PointsRuleMapping.INSTANCE::coverToDto);
    }

    /**
     * 详情查询
     *
     * @param pointsRuleId
     * @return
     */
    public PointsRuleDTO getInfo(Long pointsRuleId) {
        PointsRule pointsRule = pointsRuleService.getOne(new LambdaQueryWrapper<PointsRule>()
                .eq(PointsRule::getPointsRuleId, pointsRuleId)
                .eq(PointsRule::getDeletedVersion, 0L));
        if (Objects.isNull(pointsRule)) {
            return null;
        }
        return PointsRuleMapping.INSTANCE.coverToDto(pointsRule);
    }

    public UserPointsTaskDTO getInfoByCode(String ruleCode) {
        PointsRule pointsRule = pointsRuleService.getOne(new LambdaQueryWrapper<PointsRule>()
                .eq(PointsRule::getRuleCode, ruleCode)
                .eq(PointsRule::getDeletedVersion, 0L));
        if (Objects.isNull(pointsRule)) {
            return UserPointsTaskDTO.builder().remainderTimesPerDay(0).enable(false).build();
        }
        UserPointsTaskDTO userPointsTaskDTO = new UserPointsTaskDTO(pointsRule);
        Long userId = SecurityUtils.getUserId();
        List<PointsDetail> pointsDetails = pointsDetailService.lambdaQuery()
                .eq(PointsDetail::getSourceCode, userPointsTaskDTO.getRuleCode())
                .eq(PointsDetail::getCreatedBy, userId).eq(PointsDetail::getDeletedVersion, 0)
                .list();
        dealRemainderTimes(pointsRule, userPointsTaskDTO, pointsDetails);
        return userPointsTaskDTO;
    }

    /**
     * 处理剩余次数
     *
     * @param pointsRule        积分规则
     * @param userPointsTaskDTO 用户积分任务
     * @param pointsDetails     积分详情
     */
    private void dealRemainderTimes(PointsRule pointsRule, UserPointsTaskDTO userPointsTaskDTO, List<PointsDetail> pointsDetails) {
        if (pointsRule.getHasEnable() == 1) {
            userPointsTaskDTO.setRemainderTimesPerDay(0);
            userPointsTaskDTO.setEnable(false);
        } else {
            if (pointsRule.getMaxTimes() == -1 && pointsRule.getMaxPointsValue() == -1) {
                userPointsTaskDTO.setEnable(true);
                userPointsTaskDTO.setRemainderTimesPerDay(-1);
            } else {
                int totalRemainderTimes = pointsRule.getMaxTimes() == -1 ? Integer.MAX_VALUE : pointsRule.getMaxTimes() - pointsDetails.size();
                int todayRemainderTimes = pointsRule.getMaxPointsValue() == -1 ? Integer.MAX_VALUE : (int) (pointsRule.getTimesPerDay() - pointsDetails.stream().
                        filter(detail -> DateUtil.beginOfDay(DateUtil.date()).before(detail.getCreatedTime()))
                        .count());
                int remainderTimesPerDay = Math.min(totalRemainderTimes, todayRemainderTimes);
                userPointsTaskDTO.setRemainderTimesPerDay(remainderTimesPerDay);
                userPointsTaskDTO.setEnable(userPointsTaskDTO.getRemainderTimesPerDay() > 0);
            }
        }
    }

    /**
     * 处理剩余次数
     *
     * @param item              积分规则
     * @param pointsRuleTaskDTO 积分任务
     * @param usedRecords       已使用的积分记录
     */
    private void dealRemainderTimes(PointsRuleDTO item, PointsRuleTaskDTO pointsRuleTaskDTO, List<PointsDetail> usedRecords) {
        // B-代码逻辑
        if (item.getHasEnable() == 1) {
            pointsRuleTaskDTO.setRemainderTimesPerDay(0L);
            pointsRuleTaskDTO.setEnable(false);
        } else {
            if (item.getMaxTimes() == -1 && item.getMaxPointsValue() == -1) {
                pointsRuleTaskDTO.setEnable(true);
                pointsRuleTaskDTO.setRemainderTimesPerDay(-1L);
            } else {
                long totalRemainderTimes = item.getMaxTimes() == -1 ? Integer.MAX_VALUE : item.getMaxTimes() - usedRecords.size();
                // 上面的代码中，在重构的时候，缺少下面的逻辑处理
                long todayRemainderTimes = item.getMaxPointsValue() == -1 ? Integer.MAX_VALUE : item.getTimesPerDay() - usedRecords.stream().
                        filter(detail -> DateUtil.beginOfDay(DateUtil.date()).before(detail.getCreatedTime()))
                        .count();
                Long remainderTimesPerDay = Math.min(totalRemainderTimes, todayRemainderTimes);
                pointsRuleTaskDTO.setRemainderTimesPerDay(remainderTimesPerDay);
                pointsRuleTaskDTO.setEnable(pointsRuleTaskDTO.getRemainderTimesPerDay() > 0);
            }
        }
    }

    /**
     * 规则启用或禁用
     *
     * @param pointsRuleId
     * @param hasEnable
     * @return
     */
    public Boolean ruleEnable(Long pointsRuleId, Integer hasEnable) {
        // 修改前检查是否禁止修改
        checkRuleModificationProhibited(pointsRuleId);
        boolean flag = pointsRuleService.update(new LambdaUpdateWrapper<PointsRule>()
                .eq(PointsRule::getPointsRuleId, pointsRuleId)
                .eq(PointsRule::getDeletedVersion, 0L)
                .set(PointsRule::getHasEnable, hasEnable)
                .set(PointsRule::getUpdatedBy, SecurityUtils.getUserId())
                .set(PointsRule::getUpdatedName, SecurityUtils.getUsername())
                .set(PointsRule::getUpdatedTime, new Date())
        );
        // 缓存数据更新
        if (Boolean.TRUE.equals(flag)) {
            // 查询已存储数据
            PointsRule pointsRuleDB = pointsRuleService.getOne(new LambdaQueryWrapper<PointsRule>()
                    .eq(PointsRule::getPointsRuleId, pointsRuleId)
                    .eq(PointsRule::getDeletedVersion, 0L)
            );
            pointRuleConfigCacheService.updateCache(PointsRuleMapping.INSTANCE.coverToDto(pointsRuleDB));
        }
        return flag;
    }

    /**
     * 数据保存
     *
     * @param req
     * @return
     */
    public Boolean saveOrUpdate(PointsRuleReq req) {
        PointsRule pointsRule = PointsRuleMapping.INSTANCE.coverToEntity(req);
        // 计算 最大可获取积分
        pointsRule.setMaxPointsValue(pointsRule.getPointsValue() * pointsRule.getTimesPerDay());
        if (Objects.isNull(req.getPointsRuleId())) {
            pointsRule.setPointsRuleId(IdWorker.getId());
            pointsRule.setCreatedBy(SecurityUtils.getUserId());
            pointsRule.setCreatedTime(new Date());
        } else {
            // 修改前检查是否禁止修改
            checkRuleModificationProhibited(req.getPointsRuleId());
            pointsRule.setUpdatedBy(SecurityUtils.getUserId());
            pointsRule.setUpdatedTime(new Date());
            pointsRule.setUpdatedName(SecurityUtils.getUsername());
            // 不能修改已存在数据的编码
            PointsRule pointsRuleDB = pointsRuleService.getOne(new LambdaQueryWrapper<PointsRule>()
                    .eq(PointsRule::getPointsRuleId, pointsRule.getPointsRuleId())
                    .eq(PointsRule::getRuleCode, pointsRule.getRuleCode())
                    .eq(PointsRule::getDeletedVersion, 0L)
            );
            if (Objects.isNull(pointsRuleDB)) {
                throw new ServiceException("不能修改已存在规则的编码");
            }
        }
        boolean flag = pointsRuleService.saveOrUpdate(pointsRule);
        // 缓存数据更新
        if (Boolean.TRUE.equals(flag)) {
            // 查询已存储数据
            PointsRule pointsRuleDB = pointsRuleService.getOne(new LambdaQueryWrapper<PointsRule>()
                    .eq(PointsRule::getPointsRuleId, pointsRule.getPointsRuleId())
                    .eq(PointsRule::getDeletedVersion, 0L)
            );
            pointRuleConfigCacheService.updateCache(PointsRuleMapping.INSTANCE.coverToDto(pointsRuleDB));
        }
        return flag;
    }

    /**
     * 阅读时长积分获取
     *
     * @return
     */
    public boolean browseDurationHome() {
        Long userId = SecurityUtils.getUserId();
        if (Objects.isNull(userId) || userId == 0L) {
            return false;
        }
        IPointsDetailStrategy pointsDetailStrategy = pointsDetailStrategyMap.get(PointRuleConstant.BROWSE_DURATION_HOME + IPointsDetailStrategy.SUFFIX);
        pointsDetailStrategy.updatePoints(userId, userId);
        return true;
    }

    public boolean pointsRuleHidden(Long pointsRuleId, Integer hide) {
        // 修改前检查是否禁止修改
        checkRuleModificationProhibited(pointsRuleId);
        // 显示
        pointsRuleService.update(new LambdaUpdateWrapper<PointsRule>()
                .eq(PointsRule::getPointsRuleId, pointsRuleId)
                .set(PointsRule::getHasHide, hide)
                .set(PointsRule::getUpdatedBy, SecurityUtils.getUserId())
                .set(PointsRule::getUpdatedTime, new Date())
        );
        return true;
    }

    /**
     * 检查是否禁止修改规则, false允许修改，true禁止修改
     * @param pointsRuleId
     * @return
     */
    public void checkRuleModificationProhibited(Long pointsRuleId) {
        if (CollUtil.isEmpty(pointsRuleConfig.getDisabledRuleList())) {
            return ;
        }
        if (pointsRuleService.lambdaQuery()
                .eq(PointsRule::getPointsRuleId, pointsRuleId)
                .in(PointsRule::getRuleCode, pointsRuleConfig.getDisabledRuleList())
                .count() > 0) {
            throw new ServiceException("禁止修改本条积分规则");
        }
    }

}
